package com.glodon.paas.account.security.cas.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

import com.glodon.paas.account.biz.UserBiz;
import com.glodon.paas.account.dao.domain.UserEntity;
import com.glodon.paas.account.security.spring.LoginUser;
import com.glodon.paas.consts.StringConst;
import com.glodon.paas.exception.PaasServerExceptionFactory;
import com.glodon.paas.util.SimpleBeanConverter;

/**
 * @author Hai Chang
 */
public class InternalLoginFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(InternalLoginFilter.class);;

    public static final String INTERNAL_LOGIN_USER = "INTERNAL_LOGIN_USER";

    @Autowired
    private UserBiz userBiz;

    @Value("${api.internal.allow.ips}")
    private String allowIPs;

    //  @Value("${api.internal.allow.regex}")
    //  private String matchTemplete;

    private Set<String> ips = null;

    private Set<String> ipSegments = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ips = new HashSet<String>();
        ipSegments = new HashSet<String>();
        if (StringUtils.isNotBlank(allowIPs)) {
            ips.clear();
            ipSegments.clear();
            String[] ipss = allowIPs.split(StringConst.SEPARATOR_COMMA);
            for (String ip : ipss) {
                ip = ip.trim();
                if (ip.indexOf('-') > 0) {
                    ipSegments.add(ip);
                } else {
                    ips.add(ip);
                }
            }
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        if (!trust(request)) {
            chain.doFilter(request, response);
        } else {
            HttpServletRequest httpReq = (HttpServletRequest) request;
            String internalLoginUser = httpReq.getHeader(INTERNAL_LOGIN_USER);
            if (internalLoginUser != null) {
                Authentication auth = SecurityContextHolder.getContext().getAuthentication();
                if (auth == null) {
                    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(1);
                    authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
                    UserEntity userEntity = userBiz.getUser(internalLoginUser);
                    if (userEntity == null) {
                        chain.doFilter(request, response);
                        return;
                    }
                    LoginUser user = SimpleBeanConverter.converte(userEntity, LoginUser.class);
                    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, null,
                            authorities);
                    token.setDetails(new WebAuthenticationDetails(httpReq));

                    SecurityContextHolder.clearContext();
                    SecurityContextHolder.getContext().setAuthentication(token);
                }
                request.setAttribute("springSecurityFilterChain.SKIPPED", Boolean.TRUE);
            }
            chain.doFilter(request, response);
        }
    }

    private boolean trust(ServletRequest request) throws ServletException {
        HttpServletRequest httpReq = (HttpServletRequest) request;
        if (ips == null || ipSegments == null) {
            init(null);
        }
        String rip = httpReq.getHeader(StringConst.X_FORWARDED_FOR);
        if (StringUtils.isBlank(rip)) {
            rip = httpReq.getRemoteAddr();
        }
        if (StringUtils.isBlank(rip)) {
            throw PaasServerExceptionFactory.accessForbidden(
                    "Can not find the remote IP address, No privilege to access internal API.", null);
        }
        String[] rips = rip.split(StringConst.SEPARATOR_COMMA);
        boolean pass = false;
        for (String ip : rips) {
            if (!pass && !ips.isEmpty()) {
                pass = ips.contains(ip);
                //              if(!res){
                //                  logger.debug("{} not contain ip:{}", ips, ip);
                //              }
            }

            if (!pass && !ipSegments.isEmpty()) {
                for (String ipSegment : ipSegments) {
                    if (ipIsValid(ipSegment, ip)) {
                        pass = true;
                        break;
                    }
                }
            }
            //          if(res && StringUtils.isNotBlank(matchTemplete)){
            //              Pattern p = Pattern.compile(matchTemplete);
            //              res = p.matcher(ip).find();
            //              if(!res){
            //                  logger.debug("Regular expression {} can't match ip:{}", matchTemplete, ip);
            //              }
            //          }

            if (!pass) {
//                logger.warn("IP {} has no privilege to access internal API", ip);
                break;
            }
        }
        return pass;
    }

    private boolean ipIsValid(String ipSection, String ip) {
        try {
            int idx = ipSection.indexOf('-');
            String[] sips = ipSection.substring(0, idx).split("\\.");
            String[] sipe = ipSection.substring(idx + 1).split("\\.");
            String[] sipt = ip.split("\\.");
            long ips = 0L, ipe = 0L, ipt = 0L;
            for (int i = 0; i < 4; ++i) {
                ips = ips << 8 | Integer.parseInt(sips[i]);
                ipe = ipe << 8 | Integer.parseInt(sipe[i]);
                ipt = ipt << 8 | Integer.parseInt(sipt[i]);
            }
            if (ips > ipe) {
                long t = ips;
                ips = ipe;
                ipe = t;
            }
            return ips <= ipt && ipt <= ipe;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void destroy() {
    }
}
